// [CodeTint.js]
// Source code highlighting in javascript, (c) 2008-2009, Petr Kobalicek
//
// [License]
// - MIT
//
// [Dependencies]
// - BLite.js <http://code.google.com/p/blite/>

// ===========================================================================
// [CodeTint - Dependencies]
// ===========================================================================

if (!BLite) throw new Error("CodeTint needs BLite library to work!");

// ===========================================================================
// [CodeTint - Wrap]
// ===========================================================================

(function() {

// ===========================================================================
// [CodeTint - Constants]
// ===========================================================================

// Char codes.
var EOL = "\n"; // End of line.
var EOI = 0;    // End of Input.

// Common states (for finite state machines). States are generally shared between
// all machines, because they are mostly similar. Special states for some contexts
// should be defined at the end.
var STATE_PLAIN = 0;
var STATE_STRING = 1;
var STATE_WORD = 2;
var STATE_NUMBER = 3;
var STATE_PREPROCESSOR = 4;
var STATE_SINGLE_LINE_COMMENT = 5;
var STATE_MULTI_LINE_COMMENT = 6;

// Special states.
var STATE_XML_TAG = 10;

// Styles.
var STYLE_PLAIN = "";
var STYLE_SYMBOL = "code-symbol";
var STYLE_STRING = "code-string";
var STYLE_LWORD = "code-lword";
var STYLE_UWORD = "code-uword";
var STYLE_PWORD = "code-pword";
var STYLE_KEYWORD = "code-keyword";
var STYLE_NUMBER = "code-number";
var STYLE_PREPROCESSOR = "code-preprocessor";
var STYLE_COMMENT = "code-comment";
var STYLE_INVALID = "code-invalid";

// ===========================================================================
// [CodeTint]
// ===========================================================================

// @class CodeTint
//
// CodeTint is main class that is usually used to do syntax highlighting. This
// class contains only static methods that can be used to start processing, add
// or remove modules and add or remove html tags that can be highlighted.
//
// This class is mostly used like this way:
//
// @code
// CideTint.addAll().process();
// @endcode
//
// If you have modules thats not part of CodeTint distribution, you must first
// register them using CodeTint.addModule() method. If you have some specific
// tags where syntax highlighting should be done, use CodeTint.addTags() and
// CodeTint.removeTags() methods. Default tags are <pre> and <code>.
BLite.Object.define("CodeTint",
{
  statics:
  {
    // @static {Function} CodeTint.process(elm?)
    //
    // CodeTint entry-point that will find all code to highlight and call matching
    // module on it.
    //
    // @param elm {DOMElement?} Element where to start (default document.body).
    // @return {CodeTint} This (Chaining support).
    process: function(elm)
    {
      elm = elm || document.body;

      var storage = [];
      var node;
      var module;

      this.__getNodes(elm, storage);

      for (var i = 0; i < storage.length; i++)
      {
        var obj = storage[i];
        node = obj.node;
        module = obj.module;

        var result = module.process(BLite.Dom.getText(node));

        // In IE, the text must be int <pre> tag to be correctly displayed
        if (BLite.Browser.IE)
        {
          node.innerHTML = "";

          var pre = BLite.Dom.DIV({html: "<" + node.tagName + ">" + result + "</" + node.tagName + ">"}).firstChild;
          var e;

          while (e = pre.firstChild)
          {
            pre.removeChild(e);
            node.appendChild(e);
          }
        }
        else
        {
          node.innerHTML = result;
        }

        // remove references
        obj.node = null;
        obj.module = null;
      }

      node = null;
      module = null;
      
      // Chaining support.
      return this;
    },

    // @static CodeTint.addModule(name, inst)
    //
    // Add new module instance @a inst to the list.
    //
    // @param name {String} The name of module (default is inst.getName()).
    // @param inst {CodeTint.Module} Instance of CodeTint.Module to add.
    // @return {CodeTint} This (Chaining support).
    addModule: function(name, inst)
    {
      // {DEBUG-MODE-BEGIN}
      if (!(inst instanceof CodeTint.Module))
      {
        throw new Error("inst must be CodeTint.Module instance");
      }
      // {DEBUG-MODE-END}

      this.__modules[name || inst.getName()] = inst;

      // Chaining support.
      return this;
    },

    // @static CodeTint.removeModule(name)
    //
    // Remove module which name is @a name.
    //
    // @param name {String} The name of module instance to remove.
    // @return {CodeTint} This (Chaining support).
    removeModule: function(name)
    {
      if (this.__modules[name]) delete this.__modules[name];

      // Chaining support.
      return this;
    },

    // @static CodeTint.getModule(name)
    //
    // Get module which name is @a name.
    //
    // @param name {String} The name of module instance to get.
    // @return {CodeTint.Module?} CodeTint.Module instance or null.
    getModule: function(name)
    {
      return this.__modules[name] || null;
    },

    // @static CodeTint.addTags(tags)
    //
    // Add element tags that will be processed (default tags are <pre>
    // and <code>)
    //
    // @param tags {String[]} The tags to add.
    // @return {CodeTint} This (Chaining support).
    addTags: function(tags)
    {
      if (!BLite.Lang.isArray(tags)) tags = [tags];

      for (var i = 0, len = tags.length; i < len; i++)
      {
        this.__tags[tags[i]] = true;
      }

      // Chaining support.
      return this;
    },

    // @static CodeTint.addTags(tags)
    //
    // Remove element tags from tags list.
    //
    // @param tags {String[]} The tags to remove.
    // @return {CodeTint} This (Chaining support).
    removeTags: function(tags)
    {
      if (!BLite.Lang.isArray(tags)) tags = [tags];

      for (var i = 0, len = tags.length; i < len; i++)
      {
        if (this.__tags[tags[i]]) delete this.__tags[tags[i]];
      }

      // Chaining support.
      return this;
    },

    // @static CodeTint.hasTag(tag)
    //
    // Test whether internal tag list contains tag @a tag.
    //
    // @param tag {String} The tag to test.
    // @return {Boolean} Whether tag is in the tag list.
    hasTag: function(tag)
    {
      return this.__tags[tag] || false;
    },

    // @static CodeTint.addAll()
    //
    // Convenience method that adds registers all CodeTint modules
    // thats built-in. This is most-used function together with the
    // CodeTint.process().
    //
    // @return {CodeTint} This (Chaining support).
    addAll: function()
    {
      CodeTint.addModule("c", new CodeTint.CModule());
      CodeTint.addModule("js", new CodeTint.JSModule());
      CodeTint.addModule("shell", new CodeTint.ShellModule());
      CodeTint.addModule("cmake", new CodeTint.CMakeModule());

      // Chaining support.
      return this;
    },

    // Map of tags that will be processed by process(). Default tags are
    // <pre> and <code>.
    __tags: { "pre": true, "code": true },

    // Map that contains all installed modules (name / instance pairs).
    __modules: {},

    // Recursive method called from itself and from CodeTint.process().
    //
    // Recursively traverses all children and sub-children of @a elm and tries
    // to find tags specified in CodeTint.__tags map.
    __getNodes: function(root, storage)
    {
      var self = this;

      function found(node)
      {
        var name = "" + node.className;
        if (name.indexOf("lang-") === 0) name = name.substring(5, name.length);

        var module = self.__modules[name];
        if (module)
        {
          storage.push({node: node, module: module});
        }
      }

      if (document.getElementsByTagName)
      {
        for (var tag in this.__tags)
        {
          var nodes = document.getElementsByTagName(tag);
          for (var i = 0; i < nodes.length; i++) found(nodes[i]);
        }
      }
      else
      {
        for (var node = root.firstChild; node; node = node.nextSibling)
        {
          var tag = node.nodeName ? node.nodeName.toLowerCase() : "";
          if (tag && this.__tags[tag])
          {
            found(node);
          }
          else if (node.hasChildNodes())
          {
            var result = this.__getNodes(node, storage);
            if (result != null) return result;
          }
        }
      }

      return null;
    }
  }
});

// ===========================================================================
// [CodeTint.HtmlBuilder]
// ===========================================================================

// @class CodeTint.HtmlBuilder
//
// Html string builder used to buffer HTML output.
BLite.Object.define("CodeTint.HtmlBuilder",
{
  extend: BLite.Object,

  construct: function()
  {
    // Call superclass.
    this.base(arguments);

    // Prepare buffers.
    this._buf = ["<span>"];
    this._last = "";
  },

  members:
  {
    addChunk: function(str, style)
    {
      if (!str) return;

      var esc = BLite.Html.escape(str);
      if (style !== this._last)
      {
        if (this._last) this._buf.push('</span>');
        if (style) this._buf.push('<span class="', style, '">');
      }
      this._buf.push(esc);
      this._last = style;
    },

    addSymbol: function(str, symbols)
    {
      for (var i = 0; i < str.length; i++)
      {
        var c = str.charAt(i);
        this.addChunk(c, symbols[c] ? "code-" + symbols[c] : STYLE_PLAIN);
      }
    },

    addWord: function(str, keywords, alwaysLWord)
    {
      // First check for keywords.
      if (keywords[str])
      {
        this.addChunk(str, "code-" + keywords[str]);
      }
      // Set correct style that depends to first character.
      else
      {
        var first = str.charAt(0);

        if (alwaysLWord || (first >= "a" && first <= "z"))
          this.addChunk(str, STYLE_LWORD);
        else if (first >= "A" && first <= "Z")
          this.addChunk(str, STYLE_UWORD);
        else
          this.addChunk(str, STYLE_PWORD);
      }
    },

    getHtml: function()
    {
      if (this._last) this._buf.push("</span>");
      this._buf.push("</span>");

      var result = this._buf.join("");
      this._buf = [];
      return result;
    }
  }
});

// ===========================================================================
// [CodeTint.Module]
// ===========================================================================

// @class CodeTint.Module
//
// CodeTint.Module is base class for all CodeTint modules. Each module must
// extend this class or another one that extends CodeTint.Module. To implement
// your own module override process() method.
BLite.Object.define("CodeTint.Module",
{
  extend: BLite.Object,

  properties:
  {
    name: {}
  },

  construct: function(name)
  {
    // Call superclass.
    this.base(arguments);

    // Properties.
    this.setName(name);

    // Symbols map.
    this._symbols = {};

    // Keywords map.
    this._keywords = {};
  },

  members:
  {
    // ------------------------------------------------------------------------
    // [Symbols Management]
    // ------------------------------------------------------------------------

    addSymbols: function(symbols, style)
    {
      style = style || "symbol";

      for (var i = 0, len = symbols.length; i < len; i++)
        this._symbols[symbols[i]] = style;

      // Chaining support.
      return this;
    },

    removeSymbols: function(symbols)
    {
      for (var i = 0, len = symbols.length; i < len; i++)
        delete this._symbols[symbols[i]];

      // Chaining support.
      return this;
    },

    hasSymbol: function(key)
    {
      return this._symbols[key] ? true : false;
    },

    getSymbols: function()
    {
      var a = [];
      for (var key in this._symbols) a.push(key);
      return a;
    },

    // ------------------------------------------------------------------------
    // [Keywords Management]
    // ------------------------------------------------------------------------

    addKeywords: function(keywords, style)
    {
      style = style || "keyword";

      for (var i = 0, len = keywords.length; i < len; i++)
        this._keywords[keywords[i]] = style;

      // Chaining support.
      return this;
    },

    removeKeywords: function(keywords)
    {
      for (var i = 0, len = keywords.length; i < len; i++)
        delete this._keywords[keywords[i]];

      // Chaining
      return this;
    },

    hasKeyword: function(key)
    {
      return this._keywords[key] ? true : false;
    },

    getKeywords: function()
    {
      var a = []
      for (var key in this._keywords) a.push(key);
      return a;
    },

    // ------------------------------------------------------------------------
    // [CType]
    // ------------------------------------------------------------------------

    isAlpha: function(ch)
    {
      return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
    },

    isIdentifier: function(ch)
    {
      return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch === '_');
    },

    isDigit: function(ch)
    {
      return (ch >= '0' && ch <= '9');
    },

    // ------------------------------------------------------------------------
    // [Process]
    // ------------------------------------------------------------------------

    process: function(text)
    {
      return text;
    },
    
    comment: function(text)
    {
      return text;
    }
  }
});

// ===========================================================================
// [CodeTint.CSyntaxModule]
// ===========================================================================

BLite.Object.define("CodeTint.CSyntaxModule",
{
  extend: CodeTint.Module,

  construct: function(name)
  {
    // Call superclass.
    this.base(arguments, name);
  },

  members:
  {
    // ------------------------------------------------------------------------
    // [Virtuals]
    // ------------------------------------------------------------------------

    // @member {Function} CodeTint.CSyntaxModule.process(text)
    //
    // @see CodeTint.Module.process()
    process: function(text)
    {
      // Input.
      var input = text.replace(/\r/g, "");

      var length = input.length;
      if (!length) return input;

      var position = 0;
      var beginPosition = 0;

      var ch1;
      var ch2;

      // Output.
      var builder = new CodeTint.HtmlBuilder;

      // Symbols & Keywords.
      var symbols = this._symbols;
      var keywords = this._keywords;

      // State.
      var state = STATE_PLAIN;
      var stateCh;

      // Local functions.
      var changeState = function(toState)
      {
        beginPosition = position;
        state = toState;
      };

      var skipTo = function(pattern)
      {
        var i = input.indexOf(pattern, position);
        position = ((i === -1) ? length : i) - 1;
      };

      // Finite state machine.
      for (;;)
      {
        // Input is simplified to two characters at once, because we can
        // easy check double-char tokens (comments and escape sequences).
        ch1 = input.charAt(position    ) || EOI;
        ch2 = input.charAt(position + 1) || EOI;

        switch (state)
        {
          case STATE_PLAIN:
            if (ch1 === '/' && ch2 === '/')
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_SINGLE_LINE_COMMENT);

              // We can optimize searching by skipping to EOL.
              position++;
              skipTo(EOL);
            }
            else if (ch1 === '/' && ch2 === '*')
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_MULTI_LINE_COMMENT);

              // We can optimize searching for */
              position++;
              skipTo("*\/");
            }
            else if (this.isIdentifier(ch1))
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_WORD);
            }
            else if (this.isDigit(ch1))
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_NUMBER);
            }
            else if (ch1 === '\'' || ch1 === '\"')
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_STRING);
              stateCh = ch1;
            }
            else if (ch1 === '#')
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_PREPROCESSOR);

              // We can optimize searching by skipping to EOL.
              skipTo(EOL);
            }
            else if (ch1 === EOI)
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
            }
            break;
          case STATE_STRING:
            if (ch1 === '\\')
            {
              position++;
            }
            else if (ch1 === stateCh)
            {
              // position+1 will consume \" or \'
              builder.addChunk(input.substring(beginPosition, position+1), STYLE_STRING);
              beginPosition = position+1;

              state = STATE_PLAIN;
            }
            break;
          case STATE_WORD:
            if (!(this.isIdentifier(ch1) || this.isDigit(ch1)) || ch1 === EOI)
            {
              builder.addWord(input.substring(beginPosition, position), keywords);
              changeState(STATE_PLAIN);

              // Go back.
              position--;
            }
            break;
          case STATE_NUMBER:
            if (!(this.isIdentifier(ch1) || this.isDigit(ch1)) || ch1 === EOI)
            {
              builder.addChunk(input.substring(beginPosition, position), STYLE_NUMBER);
              changeState(STATE_PLAIN);

              // Go back.
              position--;
            }
            break;
          case STATE_PREPROCESSOR:
            if (ch1 === EOL || ch1 === EOI)
            {
              builder.addChunk(input.substring(beginPosition, position), STYLE_PREPROCESSOR);
              changeState(STATE_PLAIN);
            }
            break;
          case STATE_SINGLE_LINE_COMMENT:
            if (ch1 === EOL || ch1 === EOI)
            {
              builder.addChunk(input.substring(beginPosition, position), STYLE_COMMENT);
              changeState(STATE_PLAIN);
            }
            break;
          case STATE_MULTI_LINE_COMMENT:
            if ((ch1 === '*' && ch2 === '/') || ch1 === EOI)
            {
              position += 2;
              builder.addChunk(input.substring(beginPosition, position), STYLE_COMMENT);
              changeState(STATE_PLAIN);
              continue;
            }
            break;
        }

        position++;
        if (ch1 === EOI) break;
      }

      if (state != STATE_PLAIN)
      {
        builder.addChunk(input.substring(beginPosition, length), STYLE_INVALID);
      }

      return builder.getHtml();
    }
  },

  statics:
  {
    symbols:
    [
      "+", "-", "*", "/", "%", "&", "|", "^", "~", "!", "=", "?", ":", ";",
      "<", ">", ".", ",", "(", ")", "[", "]", "{", "}"
    ]
  }
});

// ===========================================================================
// [CodeTint.CModule]
// ===========================================================================

BLite.Object.define("CodeTint.CModule",
{
  extend: CodeTint.CSyntaxModule,

  construct: function(name)
  {
    // Call superclass.
    this.base(arguments, name || "lang-c");

    // C/C++ Symbols.
    this.addSymbols(CodeTint.CSyntaxModule.symbols);

    // C/C++ Keywords.
    this.addKeywords(
    [
      "asm", "auto", "bool", "break", "case", "catch", "char", "class", "const", "const_cast",
      "continue", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit",
      "extern", "false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable",
      "namespace", "new", "operator", "private", "protected", "public", "register", "reinterpret_cast",
      "return", "short", "signed", "sizeof", "static", "static_cast", "struct", "switch", "template",
      "this", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using",
      "virtual", "void", "volatile", "wchar_t", "while"
    ]);
    this.addKeywords(
    [
      "uchar", "ushort", "uint", "ulong", "int16", "uint16", "int32", "uint32", "int64", "uint64"
    ]);
  }
});

// ===========================================================================
// [CodeTint.JSModule]
// ===========================================================================

BLite.Object.define("CodeTint.JSModule",
{
  extend: CodeTint.CSyntaxModule,

  construct: function(name)
  {
    // Call superclass.
    this.base(arguments, name || "lang-js");

    // Javascript Symbols.
    this.addSymbols(CodeTint.CSyntaxModule.symbols);

    // Javascript Keywords.
    this.addKeywords(
    [
      "break", "case", "catch",  "const", "continue", "default", "delete", "do", "else",
      "enum", "false", "finally", "for", "function", "in", "instanceof", "if", "new",
      "return", "switch", "this", "throw", "true", "try", "typeof", "var", "while", "with"
    ]);
    this.addKeywords(
    [
      "onabort", "onblur", "onchange", "onclick", "onerror", "onfocus", "onload",
      "onmouseout", "onmouseover", "onhover", "onreset", "onselect", "onsubmit",
      "onunload"
    ], "js-event");
    this.addKeywords(
    [
      "Anchor", "Applet", "Area", "Array", "Boolean", "Button", "Checkbox",
      "Date", "document", "window", "Image", "FileUpload", "Form", "Frame",
      "Function", "Hidden", "Link", "MimeType", "Math", "Max", "Min", "Layer",
      "navigator", "Object", "Password", "Plugin", "Radio", "RegExp", "Reset",
      "Screen", "Select", "String", "Text", "Textarea", "Window"
    ], "js-object");
  },

  members:
  {
    isIdentifier: function(ch)
    {
      return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch === '_') || (ch == '$');
    }
  }
});

// ===========================================================================
// [CodeTint.ScriptSyntaxModule]
// ===========================================================================

// Common state machine for scripting languages and shells.
BLite.Object.define("CodeTint.ScriptSyntaxModule",
{
  extend: CodeTint.Module,

  construct: function(name)
  {
    // Call superclass.
    this.base(arguments, name);
  },

  members:
  {
    // ------------------------------------------------------------------------
    // [Virtuals]
    // ------------------------------------------------------------------------

    process: function(text)
    {
      // Input.
      var input = text.replace(/\r/g, "");

      var length = input.length;
      if (!length) return input;

      var position = 0;
      var beginPosition = 0;

      var ch;

      // Output.
      var builder = new CodeTint.HtmlBuilder;

      // Symbols & Keywords.
      var symbols = this._symbols;
      var keywords = this._keywords;

      // State.
      var state = STATE_PLAIN;
      var stateCh;

      // Local functions.
      var changeState = function(toState)
      {
        beginPosition = position;
        state = toState;
      };

      var skipTo = function(pattern)
      {
        var i = input.indexOf(pattern, position);
        position = ((i === -1) ? length : i) - 1;
      };

      // Finite state machine.
      for (;;)
      {
        ch = input.charAt(position) || EOI;

        switch (state)
        {
          case STATE_PLAIN:
            if (this.isIdentifier(ch))
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_WORD);
            }
            else if (this.isDigit(ch))
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_NUMBER);
            }
            else if (ch === '\'' || ch === '\"')
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_STRING);
              stateCh = ch;
            }
            else if (ch === '#')
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
              changeState(STATE_SINGLE_LINE_COMMENT);

              // We can optimize searching for EOL.
              position++;
              skipTo(EOL);
            }
            else if (ch === EOI)
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);
            }
            break;
          case STATE_STRING:
            if (ch === '\\')
            {
              position++;
            }
            else if (ch === stateCh)
            {
              // position + 1 will consume \" or \'
              builder.addChunk(input.substring(beginPosition, position + 1), STYLE_STRING);
              beginPosition = position+1;

              state = STATE_PLAIN;
            }
            break;
          case STATE_WORD:
            if (!(this.isIdentifier(ch) || this.isDigit(ch)) || ch === EOI)
            {
              builder.addWord(input.substring(beginPosition, position), keywords, true);
              changeState(STATE_PLAIN);

              // Go back.
              position--;
            }
            break;
          case STATE_NUMBER:
            if (!(this.isIdentifier(ch) || this.isDigit(ch)) || ch === EOI)
            {
              builder.addChunk(input.substring(beginPosition, position), STYLE_NUMBER);
              changeState(STATE_PLAIN);

              // go back
              position--;
            }
            break;
          case STATE_SINGLE_LINE_COMMENT:
            if (ch === EOL || ch === EOI)
            {
              builder.addChunk(input.substring(beginPosition, position), STYLE_COMMENT);
              changeState(STATE_PLAIN);
            }
            break;
        }

        position++;
        if (ch === EOI) break;
      }

      if (state != STATE_PLAIN)
      {
        builder.addChunk(input.substring(beginPosition, length), STYLE_INVALID);
      }

      return builder.getHtml();
    }
  },

  statics:
  {
    symbols:
    [
      "+", "-", "*", "/", "%", "&", "|", "^", "~", "!", "=", "?", ":", ";",
      "<", ">", ".", ",", "(", ")", "[", "]", "{", "}", "$"
    ]
  }
});

// ===========================================================================
// [CodeTint.ShellModule]
// ===========================================================================

// TODO: Not completed.
BLite.Object.define("CodeTint.ShellModule",
{
  extend: CodeTint.ScriptSyntaxModule,
  
  construct: function(name)
  {
    // Call superclass.
    this.base(arguments, name || "lang-shell");

    this.addSymbols(CodeTint.ScriptSyntaxModule.symbols);
  }
});

// ===========================================================================
// [CodeTint.CMakeModule]
// ===========================================================================

// TODO: Not completed.
BLite.Object.define("CodeTint.CMakeModule",
{
  extend: CodeTint.ScriptSyntaxModule,
  
  construct: function(name)
  {
    // Call superclass.
    this.base(arguments, name || "lang-cmake");

    this.addSymbols(CodeTint.ScriptSyntaxModule.symbols);
  }
});

// ===========================================================================
// [CodeTint.HtmlModule]
// ===========================================================================

// TODO: Not completed.
BLite.Object.define("CodeTint.HtmlModule",
{
  extend: CodeTint.Module,

  construct: function(name)
  {
    // Call superclass.
    this.base(arguments, name || "lang-html");

    // Html Symbols
    this.addSymbols(["<", ">", "\/", "\"", "&"]);
  },

  members:
  {
    // ------------------------------------------------------------------------
    // [Virtuals]
    // ------------------------------------------------------------------------

    isIdentifier: function(ch) { return /[\w_-]/.test(ch); },

    process: function(text)
    {
      // Input.
      var input = text.replace(/\r/g, "");
      var length = input.length;
      var position = 0;
      var beginPosition = 0;

      if (!length) return input;

      var ch;

      // Output.
      var builder = new CodeTint.HtmlBuilder;

      // Symbols & Keywords.
      var symbols = this._symbols;
      var keywords = this._keywords;

      // Local functions.
      var changeState = function(toState)
      {
        beginPosition = position;
        state = toState;
      };

      var skipTo = function(pattern)
      {
        var i = input.indexOf(pattern, position);
        position = ((i === -1) ? length : i) - 1;
      };

      // Finite state machine.
      for (;;)
      {
        ch = input.charAt(position) || EOI;

        switch (state)
        {
          case STATE_PLAIN:
            if (ch === "<")
            {
              builder.addSymbol(input.substring(beginPosition, position), symbols);

              // parse TAG

              changeState(STATE_XML_TAG);
            }
            break;

          case STATE_XML_TAG:
            break;
        }

        position++;
        if (ch === EOI) break;
      }

      if (state != STATE_PLAIN)
      {
        builder.addChunk(input.substring(beginPosition, length), STYLE_INVALID);
      }

      return builder.getHtml();
    }
  }
});

// ===========================================================================
// [CodeTint - End]
// ===========================================================================

}());
